﻿using System;
using System.Collections.Generic;
using SCG.SolarVengeanceEngine;
using System.Windows;
using SCG.General;

namespace SCG.SolarVengeance.Scenarios
{
    public class CollectTheScientists : Scenario
    {
        //constructor
        public CollectTheScientists()           
        {
            spNumSystems = CreateParameter("StarSystems", 20, 10, 200);
            spNumScientists = CreateParameter("Scientists", 7, 1, 100);
            spNumWormHoles = CreateParameter("WormHoles", 4, 0, 20);
            spNumPulsars = CreateParameter("Pulsars", 2, 0, 20);
            spNumBlackHoles = CreateParameter("BlackHoles", 0, 0, 20);
        }

        //Recommend multiplayer
        public override PlayModeRecommendation Recommendation
        {
            get
            {
                return PlayModeRecommendation.MultiPlayer;
            }
        }

        //Build the scenario
        public override void BuildScenario(int numPlayers, int capitalValues, int startingResources)
        {
            int numSystems = spNumSystems.Value;
            int numScientists = spNumScientists.Value;

            //there is a central cluster of low value stars
            int central = numSystems / 3;
            numSystems -= central;

            CreateStarCluster(central, 50, 50, 50, 50, 20, 2);
            foreach(StarSystem lvss in Game.StarSystems)
                lvss.Value = (int)(20 * SVGame.RNG.NextDouble() * SVGame.RNG.NextDouble() * SVGame.RNG.NextDouble());

            //and a central nebula, the supernova remnant
            CreateNebula(50, 50, 1, 500);

            //create the scientists!
            while (numScientists > 0)
            {
                int x = Random(35, 65);
                int y = Random(35, 65);
                UDO udo = CreateUDO("Scientist", "Scientist", x, y);
                udo.AutoPickUp = true;
                udo.Description = "Pick up a Scientist by moving one of your StarShips over it.  Move the Scientists " +
                    "into the Dropoff Zone around your Capital, then drop them off by issuing the Eject command.";               
                udo.IsTaggable = true;
                numScientists--;
            }

            //create capitals, they should be about 25 away from center, and not close to other capitals
            foreach (Player p in Game.Players)
            {
                int x = 0;
                int y = 0;
                do
                {
                    x = Random(10, 90);
                    y = Random(10, 90);
                }
                while (!CapitalLocationOK(x, y, 25));
                StarSystem cap = CreateStarSystem(x, y, 20, 10, 10, 100);
                p.Capital = cap;
                numSystems--;
            }

            //create the remainins systems
            while (numSystems > 0)
            {
                numSystems--;
                int x = 0;
                int y = 0;
                do
                {
                    x = Random(2, 98);
                    y = Random(2, 98);
                }
                while (!LocationOK(x, y, 20, 100));
                StarSystem sys = CreateStarSystem(x, y);
            }

            //Create wormholes
            int wh = spNumWormHoles.Value;
            while (wh > 0)
            {
                int x = 0;
                int y = 0;
                int destx = 0;
                int desty = 0;
                do
                {
                    x = Random(2, 98);
                    y = Random(2, 98);
                    destx = Random(2, 98);
                    desty = Random(2, 98);
                }
                while (!LocationOK(x, y, 15, 45) || !LocationOK(destx, desty, 15, 45));
                CreateWormhole(x, y, destx, desty);
                wh--;
            }

            //create the pulsars
            int ps = spNumPulsars.Value;
            while (ps > 0)
            {
                int x = 0;
                int y = 0;
                do
                {
                    x = Random(2, 98);
                    y = Random(2, 98);
                }
                while (!CapitalLocationOK(x, y, 12));
                CreatePulsar(x, y);
                ps--;
            }

            //create the black holes
            int bh = spNumBlackHoles.Value;
            while (bh > 0)
            {
                int x = 0;
                int y = 0;
                do
                {
                    x = Random(2, 98);
                    y = Random(2, 98);
                }
                while (!CapitalLocationOK(x, y, 20));
                CreateBlackHole(x, y, Random(5, 20));
                bh--;
            }
        }

        //Scenario description
        public override string Description
        {
            get
            {
                return "The Nisei-Ten Empire lies in ruins after a Supernova explosion.  It was renowned for its advanced technologies, and some of its " +
                    "scientists survived the catastrophe and can be found in isolated bases in the nebula remains.  The Star Lord who collects the most " +
                    "scientists and transfers them into their Dropoff Zone is the winner.  The game ends when all remaining scientists are in a single Dropoff Zone, " +
                    "or the Dropoff Zones of an Allied Team.";
            }
        }

        //custom rendering
        public override void CustomRendering(ISVCustomRenderer renderer)
        {
            SCGRect rect = SafeZone(Game.CurrentPlayer);
            renderer.FillRectangle(rect.X, rect.Y, rect.Right, rect.Bottom, new SCGColor(220, 255, 0, 0), new SCGColor(50, 255, 0, 0), 3);
            renderer.DrawText("Dropoff Zone", rect.X, rect.Bottom, 4, 24, SCGColor.Red, 12);
            renderer.DrawTextScreenCoords(_inSafeZone, 12, 6, SCGColor.Cyan, 12);
            renderer.DrawTextScreenCoords(_carried, 12, 26, SCGColor.Cyan, 12);
        }

        //Dynamic processing
        public override void ProcessImpulse(int impulse)
        {
            //Auto-shielding
            foreach (Player p in Game.Players)
                AutoShielding(p.Capital);
        }

        //Process the Visuals
        private string _inSafeZone = "";
        private string _carried = "";
        public override void ClientProcessImpulse(int impulse)
        {
            //count the number of UDOs in safe zones
            int n = 0;
            foreach (UDO udo in Game.UDOs)
                foreach (Player p in Game.Players)
                    if (!p.Eliminated)
                        if (SafeZone(p).Contains(udo.Location))
                            n++;
            _inSafeZone = n + " of " + Game.UDOs.Count + " Scientists in Dropoff Zone";

            int carried = 0;
            foreach (UDO udo in Game.UDOs)
                if (udo.Carrier != null)
                    carried++;
            _carried = carried + " Scientists being carried";
        }

        //Game ends when all UDOs collected
        public override bool VictoryReached()
        {
            if (_udosSafe == Game.UDOs.Count)
            {
                //one player/alliance must have all UDOs
                List<Player> playersWithUDOs = new List<Player>();
                foreach (Player p in Game.Players)
                    if (!p.Eliminated)
                    {
                        foreach (UDO udo in Game.UDOs)
                            if (SafeZone(p).Contains(udo.Location))
                            {
                                playersWithUDOs.Add(p);
                                break;
                            }
                    }

                //must be mutual allies
                bool mutualAllies = true;
                foreach(Player p1 in playersWithUDOs)
                    foreach(Player p2 in playersWithUDOs)
                        if (p1 != p2)
                        {
                            if (p1.AlliedWith(p2) != AllianceLevel.Formal)
                                mutualAllies = false;
                            else if (p2.AlliedWith(p1) != AllianceLevel.Formal)
                                mutualAllies = false;
                        }
                if (mutualAllies)
                {
                    foreach (Player winner in playersWithUDOs)
                        winner.Victor = true;
                    return true;
                }
            }

            //revert to normal VC
            return base.VictoryReached();
        }

        //private members
        private int _udosSafe = 0;
        private ScenarioParameter spNumSystems;
        private ScenarioParameter spNumScientists;
        private ScenarioParameter spNumWormHoles;
        private ScenarioParameter spNumPulsars;
        private ScenarioParameter spNumBlackHoles;

        //Get the safe zone for a player
        private SCGRect SafeZone(Player p)
        {
            SCGPoint pt = p.Capital.Location;
            pt.X -= 9;
            pt.Y -= 2;
            return new SCGRect(pt.X, pt.Y, 6, 6);
        }

        //auto-shield routine
        private void AutoShielding(StarSystem system)
        {
            if (system != null)
                if (system.Shields < 200)
                    DynamicEvents.SetShields(system, system.Shields + 1);
        }

        //is this location OK for a capital?
        private bool CapitalLocationOK(int x, int y, int minDistance)
        {
            if (!LocationOK(x, y, 25, 45))
                return false;

            foreach(Player p in Game.Players)
                if (p.Capital != null)
                {
                    double dist = p.Capital.Distance(x, y);
                    if (dist < minDistance - Game.Players.Count)
                        return false;
                }
            return true;
        }

        //is this location OK for an outer system or cosmic terrain?
        private bool LocationOK(int x, int y, int minDistance, int maxDistance)
        {
            foreach (Player p in Game.Players)
                if (p.Capital != null)
                    if (SafeZone(p).Contains(new SCGPoint(x, y)))
                        return false;

            double dist = Distance(x, y, 50, 50);
            if (dist < minDistance || dist > maxDistance)
                return false;

            return true;
        }
    }
}
